package com.loyal.client;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import retrofit2.Call;
import retrofit2.Callback;

public class ClientHelper {
    private static volatile ClientHelper mInstance;
    private final OkHttpClient.Builder okctBuilder = new OkHttpClient.Builder()
            .connectTimeout(Timeout.TIMEOUT_CONNECT, TimeUnit.SECONDS)
            .writeTimeout(Timeout.TIMEOUT_WRITE, TimeUnit.SECONDS)
            .readTimeout(Timeout.TIMEOUT_READ, TimeUnit.SECONDS)
            .pingInterval(Timeout.TIMEOUT_PING, TimeUnit.SECONDS)//webSocket 心跳机制
            ;
    private final OkHttpClient okClient = okctBuilder.build();

    private ClientHelper() {
    }

    public static ClientHelper getInstance() {
        if (mInstance == null) {
            synchronized (ClientHelper.class) {
                if (mInstance == null) {
                    mInstance = new ClientHelper();
                }
            }
        }
        return mInstance;
    }

    public OkHttpClient.Builder getOkCtBuilder() {
        return okctBuilder;
    }

    public OkHttpClient getClient() {
        return okClient;
    }

    /**
     * 同步请求
     */
    public String requestSync(@NonNull String url) throws IOException {
        Request request = new Request.Builder().url(url).build();
        return requestSync(request);
    }

    /**
     * 同步请求
     */
    public String requestSync(@NonNull Request request) throws IOException {
        try (Response response = okClient.newCall(request).execute()) {
            if (response.isSuccessful()) {
                ResponseBody body = response.body();
                return checkBody(body);
            } else throw new IOException("Unexpected code" + response);
        }
    }

    public ResponseBody download(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();
        try (Response response = okClient.newCall(request).execute()) {
            if (response.isSuccessful())
                return response.body();
            else throw new IOException("Unexpected code" + response);
        }
    }

    /**
     * 重写了WebSocketListener中的几个方法，这几个方法很好理解，是用来异步回调的，
     * 这里简单说一下：onOpen当WebSocket和远程建立连接时回调；两个onMessage就是接收到消息时回调，
     * 只是消息内容的类型不同；onClosing是当远程端暗示没有数据交互时回调（即此时准备关闭，但连接还没有关闭）；
     * onClosed就是当连接已经释放的时候被回调；onFailure当然是失败时被回调（包括连接失败，发送失败等）。
     * <p>
     * send用来发送消息；close用来关闭连接
     *
     * @param wsUrl          访问的地址，如："ws://192.168.0.110/tracall/websocket/socketServer.do?sbxlh=123456";
     * @param socketListener socket连接监听
     */
    public WebSocket asSocketConnect(@NonNull String wsUrl, WebSocketListener socketListener) {
        //建立连接
        Request request = new Request.Builder().url(wsUrl).build();
        return asSocketConnect(request, socketListener);
    }

    private @Nullable String checkBody(ResponseBody body) throws IOException {
        if (null == body)
            return null;
        return body.string();
    }

    /**
     * 使用之前必须用As
     */
    public WebSocket asSocketConnect(@NonNull Request request, @NonNull WebSocketListener socketListener) {
        //建立连接
        return okClient.newWebSocket(request, socketListener);
    }

    /**
     * @param config 访问参数配置类
     * @return <a href="http://192.168.0.155:9080/test/">...</a> 必须以"/"结尾
     */
    public static String getBaseUrl(@NonNull ClientConfig config) {
        String httpOrHttps = config.getHttpOrHttps();
        String url = config.getUrl();
        String port = config.getPort();
        String nameSpace = config.getNameSpace();
        if (!portValid(port)) {
            throw new IllegalArgumentException("端口号范围0～65535，当前端口号：" + port);
        } else {
            String ipUrl =//端口号是否为空
                    TextUtils.isEmpty(port) ? url : String.format("%s:%s", url, port);
            if (ipUrl.endsWith("/"))
                ipUrl = ipUrl.substring(0, ipUrl.lastIndexOf("/"));

            String baseUrl;
            if (TextUtils.isEmpty(nameSpace)) {
                baseUrl = String.format("%s://%s/", httpOrHttps, ipUrl);
            } else
                baseUrl = String.format("%s://%s/%s/", httpOrHttps, ipUrl, nameSpace);
            return baseUrl;
        }
    }

    public static boolean portValid(String port) {
        try {
            if (TextUtils.isEmpty(port))
                return true;
            int portInt = Integer.parseInt(port);
            return (portInt >= 0 && portInt <= 65535);
        } catch (Exception e) {
            return false;
        }
    }

    public static <T> void callExecute(Call<T> call, Callback<T> callback) {
        if (null == call)
            return;
        call.enqueue(callback);
    }

    /**
     * 有可能会出现异常
     * InterruptedIOException: executor rejected
     * 闲置的连接与线程会自动释放，也可以手动释放，
     * 调用dispatcher的executorService的shutDown方法后，后面所有的网络访问也就会被拒绝了。
     */
    public void shutdown() {
        okClient.dispatcher().executorService().shutdown();
    }

    /**
     * 取消所有请求任务
     */
    public void cancelAll() {
        okClient.dispatcher().cancelAll();
    }

    /**
     * 如果client使用了缓存，关闭缓存使用
     * <p>
     * client.cache().close();
     * 如果关闭缓存之后仍然进行网络请求，会出现异常。
     */
    public void closeCache() {
        try {
            Cache cache = okClient.cache();
            if (null != cache)
                cache.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static class Timeout {
        public static int TIMEOUT_READ = 15; //设置读取超时
        public static int TIMEOUT_WRITE = 15; //设置写入超时
        public static int TIMEOUT_CONNECT = 15;//设置连接超时
        public static int TIMEOUT_PING = 15;//设置心跳超时
    }
}